#define MSGLCD_VERSION "0.1.7"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <signal.h>
#include <string.h>
#include "libs/msgserv.h"


#define MY_NAME "msgdump"
#define PID_FILE "/var/run/msglcd.pid"
#define REFRESH 2
#define min(x,y) ((x) < (y) ? (x) : (y))
#define BUFF_LEN 256
#define CLRSCR {0x1B, 0x5B, 0x32, 0x4A, 0x1B, 0x5B, 0x30, 0x3B, 0x30, 0x48, 0x00}
#define MAX_MESG 250

#define PINFO(smt, ...) do { printf(smt, ## __VA_ARGS__ ); printf("\n"); } while(0)
#define PDEBUG(str, ...) PDEBUGL(1, str, ##__VA_ARGS__);
#define PDEBUGL(x, str, ...) do{ if ( debug >= x ) _INTERNAL_LOGIT(str, ##__VA_ARGS__); }while(0)
#define PERROR(str, ...) do { printf(MY_NAME ": (%s:%d): ", __FILE__, __LINE__); printf(str, ##__VA_ARGS__); printf("error %d:%s \n", errno, strerror(errno)); }while(0)
#define _INTERNAL_LOGIT(str, ...) do { printf( MY_NAME ": (%s:%d): ", __FILE__, __LINE__); printf(str, ##__VA_ARGS__); printf("\n"); }while(0)


const char pri2text[][4]=
{
     "EMR",
     "ALR",
     "CRI",
     "ERR",
     "WRN",
     "NTC",
     "INF",
     "DEB"
};


int messages=0;                   //number of messages stored at the server
struct message_struct *message = NULL;
in_addr_t server_ip;
int sockfd;
int debug = 5;
int active_msg,shift;
time_t refresh_time=0;

static void safe_exit()
{
	msglcd_send(sockfd,MSGLCD_MESG_EXIT,3);
	close(sockfd);
	if (message) free(message);
	exit(0);
}

#define fatal(format, ...) \
do {\
	PERROR(format, ##__VA_ARGS__);\
	safe_exit();\
} while (0)

static void sig_hup ()
{
	PINFO("Cought signal. Shutting down");
	safe_exit();
}

static void parse_cmd(int argc, char *argv[])
{
   int c=0;
   extern char *optarg;
   while (c != -1) {
      c=getopt(argc, argv, "hdvi:");
      switch (c) {
       case 'h':
         printf(MY_NAME
		": dumps messages to screen.\n"
		"\t-v\tprint version and exit.\n"
                "\t-h\tdisplay this help.\n"
                "\t-d\tdebug monde on (do not fork into the background);\n"
                "\t-i<IP address>\t server ip address.\n"
		"\t-c<IP address>\t address of the client which messages\n"
		"\t\tshould be displayed, may be specyfied multiple times\n"
		"\t-C\treset client list\n");
	 exit(0);
	case 'v':
		printf("Version: %s\n", MSGLCD_VERSION);
		exit(0);
       case 'd':
         debug++;
         break;
       case 'i':
         server_ip=inet_addr(optarg);
	 break;
      }
   }
}

static void msg_dump()
{
		char *c;
		int i,j;
		struct tm *time_rec;
		time_t tmp_time;
		const char clrscr[]=CLRSCR;
		
		printf(clrscr);
		puts("Dumping messages");
		puts("+---+---+---------------|---+---+-----+----------------------------------------+\n"
			"|msg|cli|   IP address  |nr |pri|time |              text                      |\n"
			"+---+---+---------------|---+---+-----+----------------------------------------+");
		for (i=0;i<messages;i++) {
			tmp_time = (time_t) message[i].time;
			time_rec = localtime(&tmp_time);
			printf("|%3d|%3d|", i,message[i].guest);
			c=inet_ntoa(message[i].addr);
			for (j=15-strlen(c);j>0;j--)
				putc(' ',stdout);
			printf("%s", c);
			printf("|%3d|%3d|%.2d:%.2d|", message[i].nr, message[i].pri, (*time_rec).tm_hour,(*time_rec).tm_min);
			for (j=0;j<MSG_LEN;j++) {
				if ( message[i].mesg[j]<8 )
					putchar(message[i].mesg[j]+'0');
				else
					putchar(message[i].mesg[j]);
			}
			puts("|");
		}
		puts("+---+---+---------------|---+---+-----+----------------------------------------+");
	
}

static int msg_shift(int mesg, int direct)
{
	if ( (mesg<0) || (mesg>=messages) )
		return(-1);
	memcpy(&(message[mesg+direct]),&(message[mesg]),sizeof (struct message_struct));
	return(0);
}

static int msg_delete(int mesg)
{
	int i;
	if ( (mesg < 0) || (mesg > messages) )  {
		PERROR("error in msgdelete");
		return(-1);
	}
	--messages;
	/* copy messages to remove the last messege[i] */
	for(i = mesg; i < messages; ++i) {
		memcpy(&(message[i]), &(message[i+1]), sizeof(*message));
	}
	/* deallocate memory */
	message = realloc(message, sizeof(*message)*messages);
	if ( messages && NULL == message ) fatal("error allocating memory");

	return(0);
}

static int msg_find(int client, char nr)
{
	int i;
	for ( i = 0 ; i < messages ; ++i )
		if ( ( message[i].guest == client ) && ( message[i].nr == nr ) ) {
			return (i);
	}
	return (i);
}

#define reorder_test(mesg_a,mesg_b) \
	( ( mesg_a.pri>mesg_b.pri ) || \
	 ( ( mesg_a.pri==mesg_b.pri ) && ( mesg_a.guest > mesg_b.guest ) ) || \
	 ( ( mesg_a.pri==mesg_b.pri ) && ( mesg_a.guest == mesg_b.guest ) && ( mesg_a.nr>mesg_b.nr ) ) )


static int msg_order_new_message(struct message_struct new_mesg)
{
	int i;
	i = messages;
	if (messages == MAX_MESG) {
		if (debug) printf("Too many messages.");
		return(-1);
	}
	/* alloc new space for message */
	++messages;
	message = realloc(message, sizeof(*message)*messages);
	if (NULL == message ) fatal("error allocating memory");
	
	/* order this new message */
	if ( messages > 1 ) {
		if ( ( reorder_test( message[i-1], new_mesg)) ) {
			do {
				msg_shift(--i, +1);
			} while ( ( i>0 ) && ( reorder_test ( message[i-1], new_mesg ) ) );
		}
	} else {	
		//on_display[0][0] = 1;
	}
	
	return i;
}

static int msg_meng(char *buff, int len)
{
	int i;
	switch (buff[1]) {
	case MSG_UP:
		switch ( len ) {
		case 3 + sizeof (struct message_struct):
			i = msg_find((*((struct message_struct *) &(buff[3]))).guest,
				(*((struct message_struct *) &(buff[3]))).nr);
			if ( debug ) {
				if ( i == messages ) PDEBUGL(3, "Adding:");
				else PDEBUGL(3, "Updating:");
			}
			if (i == messages) {
				i = msg_order_new_message((*((struct message_struct *) &(buff[3]))));
				if ( i < 0 ) return(-3);
			}
			memcpy(&(message[i]), buff+3, sizeof (struct message_struct));
			break;
		case 3 + sizeof (struct message_struct) - MSG_LEN:
			if ( (i=msg_find((*((struct message_struct *) &(buff[3]))).guest,
				(*((struct message_struct *) &(buff[3]))).nr) ) == messages ) {
				PDEBUG("Message not found.");
				return(-1);
			}
			PDEBUG("Deleting mesg # %d",i);
			msg_delete(i);
			break;
		default:
			PDEBUG("invalid lenght.");
			return(-1);
		}
		break;
	case MSG_DEL:
		if ( len != 7 ) {
			PINFO("invalid lenght of packet.");
			return(-1);
		}
		PDEBUG("Deletting messages from client %d",*( (int *) &(buff[3])));
		i=0;
		while (i<messages) {
			if ( message[i].guest == *( (int *) &(buff[3])) )
				msg_delete(i);
			else
				i++;
		}
		break;	
	case MSG_LCD:
		//if ( buff[2] != '#' ) return(-10);
		//if ( buff[3] != '#' ) return(-10);
		//if ( buff[4] != '#' ) return(-10);
		//DEBUGL(5, "Sending a message straight to lcds.");
		break;
	default:
		puts("unknown packet");
		return(-1);
	}
	return(0);
}
//----------------------msg end---------------------

static int wait_for_any_message()
{
	fd_set set;
	FD_ZERO(&set);
	FD_SET(sockfd,&set);
	/* let's wait forever */
	return(select(sockfd+1,&set,NULL,NULL,NULL));
}

static void reconnect_on_error(const char *format, ...)
{
	PINFO(format);
	close(sockfd);
	sleep(1);
	sockfd = msglcd_socket_connect(server_ip, MSG_CONNECT_LCD);
	if ( sockfd >= 0 ) return;
	PERROR("Could not reconnect. Exiting");
	messages=0;
	safe_exit();
}

int main(int argc, char *argv[])
{
	char buff[256];
	int i;
	
	server_ip=inet_addr("127.0.0.1");
	parse_cmd(argc,argv);
	
	signal(SIGHUP, sig_hup);
	signal(SIGQUIT,sig_hup);
	signal(SIGTERM,sig_hup);
	signal(SIGINT,sig_hup);
	
	sockfd = msglcd_socket_connect(server_ip, MSG_CONNECT_LCD);
	if ( sockfd < -1 ) 
	{
		fatal("lcd_socket_connect");
	}
	
	
	msg_dump();
	
	for (;;) {
	 
		if ( wait_for_any_message() == -1 ) {
			reconnect_on_error("test_fd error.");
			continue;
		}
		
		i = msglcd_recv(sockfd, buff, 256);
		switch ( i ) {
		case -1:
			reconnect_on_error("internal error");
			continue;
		case -2:
			reconnect_on_error("buff size too small. Probably a bigger error");
			continue;
		case -3:
			reconnect_on_error("invalid messege length");
			continue;
		case -4:
			reconnect_on_error("Parity error while receiving from the client %d.");
			continue;
		case -5:
			reconnect_on_error("serverclosed connection to me.");
			continue;
		case 7:
			if ( !strncmp(buff+2, MSGLCD_MESG_OK, 3) ) {
				PDEBUGL(2,"server sended ok message");
				continue;
			} else if ( !strncmp(buff+2, MSGLCD_MESG_EXIT, 3) ) {
				reconnect_on_error("Server closed the connection.");
			} else if ( !strncmp(buff+2, MSGLCD_MESG_PING, 3) ) {
				PDEBUGL(2,"pinged. sending ok");
				if ( msglcd_send(sockfd,MSGLCD_MESG_OK,3) < 0 ) {
					reconnect_on_error("error sending the replay");
				}
			} else {
				reconnect_on_error("Strange mesg received from the server");
			}
			continue;
		}
				
		if ( msg_meng(buff+2,i-4) < 0 ) {
			reconnect_on_error("Error processing of the request.");
			continue;
		}
		
		if ( msglcd_send(sockfd,MSGLCD_MESG_OK,3) < 0 ) {
			reconnect_on_error("error sending the replay");
			continue;
		}
		
		
		msg_dump();
	}
	
	return(0);
}

